home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 11 Learning / 01 Manslow / GAPBILExample / GAPBILExampleDoc.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-10  |  14.1 KB  |  569 lines

  1. //GAPBILExample
  2. //Copyright John Manslow
  3. //29/09/2001
  4.  
  5. // GAPBILExampleDoc.cpp : implementation of the CGAPBILExampleDoc class
  6. //
  7.  
  8. #include "stdafx.h"
  9. #include "GAPBILExample.h"
  10.  
  11. #include "GAPBILExampleDoc.h"
  12.  
  13. #include "CWorld.h"
  14. #include "CPBIL.h"
  15. #include "CGA.h"
  16. #include "CPS.h"
  17.  
  18. class CGAPBILExampleView;
  19.  
  20. #ifdef _DEBUG
  21. #define new DEBUG_NEW
  22. #undef THIS_FILE
  23. static char THIS_FILE[] = __FILE__;
  24. #endif
  25.  
  26. /////////////////////////////////////////////////////////////////////////////
  27. // CGAPBILExampleDoc
  28.  
  29. IMPLEMENT_DYNCREATE(CGAPBILExampleDoc, CDocument)
  30.  
  31. BEGIN_MESSAGE_MAP(CGAPBILExampleDoc, CDocument)
  32.     //{{AFX_MSG_MAP(CGAPBILExampleDoc)
  33.         // NOTE - the ClassWizard will add and remove mapping macros here.
  34.         //    DO NOT EDIT what you see in these blocks of generated code!
  35.     //}}AFX_MSG_MAP
  36. END_MESSAGE_MAP()
  37.  
  38. extern CGAPBILExampleView *pView;
  39.  
  40. //Pointer to the world in which the agent will be evaluated
  41. CWorld *pWorld;
  42.  
  43. //The optimisation algorithms that can be used
  44. CGA *pOptimiser;
  45. //CPBIL *pOptimiser;
  46. //CPS *pOptimiser;
  47.  
  48. //The directions the agent is facing 0 - 4 = E N W S
  49. int nFacing;
  50. long dx,dy;
  51.  
  52. //The health (performance) of the current agent
  53. double dHealth=0.0;
  54.  
  55. //The number of rules that can be used
  56. unsigned long ulNumberOfRules=6;
  57.  
  58. //The number of performance measurements that have been made
  59. unsigned long ulIteration=0;
  60.  
  61. //The number of iterations used in evaluating the current rule base
  62. unsigned long ulEvaluationIteration=0;
  63.  
  64. //The chromosome currently under evaluation
  65. double *pdChromosome=NULL;
  66.  
  67. //The quantity of food and poison eaten thus far
  68. unsigned long ulFoodEaten=0;
  69. unsigned long ulPoisonEaten=0;
  70.  
  71. //Whether we're running in slow motion - i.e. juts watching the behaviour of the best rule base
  72. int nSlowMotion=0;
  73.  
  74. /////////////////////////////////////////////////////////////////////////////
  75. // CGAPBILExampleDoc construction/destruction
  76.  
  77. CGAPBILExampleDoc::CGAPBILExampleDoc()
  78. {
  79.     unsigned long i;
  80.  
  81.     //Seed the random number generator
  82.     srand(unsigned(time(NULL)));
  83.  
  84.     //Create the world
  85.     pWorld=new CWorld(50,50);
  86.  
  87.     //This array contains information about the types required for each gene in the optimisation algorithm. In this 
  88.     //case, they all all binary - that is, ot type zero
  89.     int *pnTypes=new int[16*ulNumberOfRules+3];
  90.     for(i=0;i<16*ulNumberOfRules+3;i++)
  91.     {
  92.         pnTypes[i]=0;
  93.     }
  94.  
  95.     //Create whichever type of optimisation algorithm is required
  96.     pOptimiser=new CGA(50,8*ulNumberOfRules+3,pnTypes);
  97. //    pOptimiser=new CPBIL(50,8*ulNumberOfRules+3);
  98. //    pOptimiser=new CPS(8*ulNumberOfRules+3,pnTypes);
  99.  
  100.     //Clean up
  101.     delete []pnTypes;
  102.  
  103.     //Load an optimisation algorithm if there's one pre-saved
  104. //        pOptimiser->Load("GA.txt");
  105. //        pOptimiser->Load("PBIL.txt");
  106. //        pOptimiser->Load("PS.txt");
  107. }
  108.  
  109. CGAPBILExampleDoc::~CGAPBILExampleDoc()
  110. {
  111.     delete pOptimiser;
  112.     delete pWorld;
  113.     delete []pdChromosome;
  114. }
  115.  
  116. void TurnLeft(void)
  117. {
  118.     //Rotates to the left
  119.     nFacing--;
  120.     if(nFacing<0)
  121.     {
  122.         nFacing=3;
  123.     }    
  124. }
  125.  
  126. void TurnRight(void)
  127. {
  128.     //Rotates to the right
  129.     nFacing++;
  130.     if(nFacing>3)
  131.     {
  132.         nFacing=0;
  133.     }    
  134. }
  135.  
  136. int nLookForward(void)
  137. {
  138.     long lx,ly;
  139.  
  140.     //Set displacements into the world array according the direction we're facing
  141.     if(nFacing==0)
  142.     {
  143.         dx=1;
  144.         dy=0;
  145.     }
  146.     if(nFacing==1)
  147.     {
  148.         dx=0;
  149.         dy=-1;
  150.     }
  151.     if(nFacing==2)
  152.     {
  153.         dx=-1;
  154.         dy=0;
  155.     }
  156.     if(nFacing==3)
  157.     {
  158.         dx=0;
  159.         dy=1;
  160.     }
  161.  
  162.     //Convert the displacements into indices into the world array
  163.     lx=pWorld->ulCharacterLocationX+dx;
  164.     ly=pWorld->ulCharacterLocationY+dy;
  165.  
  166.     //If we're looking beyond the edge of the world, return "wall"
  167.     if(lx<1 || ly<1 || lx>=long(pWorld->ulWorldSizeX) || ly>=long(pWorld->ulWorldSizeY))
  168.     {
  169.         return 2;
  170.     }
  171.  
  172.     //Otherwise return the contents of the world array
  173.     return pWorld->ppnWorld[lx][ly];
  174. }
  175.  
  176.  
  177. int nLookLeft(void)
  178. {
  179.     int ReturnValue;
  180.         
  181.     //We look left by tunring left, looking and turning back
  182.     TurnLeft();
  183.     ReturnValue=nLookForward();
  184.     TurnRight();
  185.     return ReturnValue;
  186. }
  187.  
  188. int nLookRight(void)
  189. {
  190.     int ReturnValue;
  191.  
  192.     //We look right by tunring right, looking and turning back;
  193.     TurnRight();
  194.     ReturnValue=nLookForward();
  195.     TurnLeft();
  196.     return ReturnValue;
  197. }
  198.  
  199.  
  200. void MoveForward(void)
  201. {
  202.     double dScale=10.0*4.0/5.0;
  203.     //Create a drawing surface
  204.     CClientDC *pDC=new CClientDC((CView*)pView);
  205.  
  206.     //If we're running in slow motion, its so that we can see the motion of the agent - so we bettwer display it.
  207.     //Since the agent is about to move, we better remove it from the display
  208.     if(nSlowMotion)
  209.     {
  210.         pDC->SelectStockObject(WHITE_PEN);
  211.         pDC->SelectStockObject(WHITE_BRUSH);
  212.         pDC->Rectangle(
  213.                                     int(pWorld->ulCharacterLocationX*dScale),
  214.                                     int(pWorld->ulCharacterLocationY*dScale),
  215.                                     int((pWorld->ulCharacterLocationX+1)*dScale+1),
  216.                                     int((pWorld->ulCharacterLocationY+1)*dScale+1));
  217.     }
  218.  
  219.     //Work out which way we're going to move from which way we're facing
  220.     if(nFacing==0)
  221.     {
  222.         dx=1;
  223.         dy=0;
  224.     }
  225.     if(nFacing==1)
  226.     {
  227.         dx=0;
  228.         dy=-1;
  229.     };
  230.     if(nFacing==2)
  231.     {
  232.         dx=-1;
  233.         dy=0;
  234.     }
  235.     if(nFacing==3)
  236.     {
  237.         dx=0;
  238.         dy=1;
  239.     }
  240.  
  241.     //See whether we've eaten any food, poison, etc. and update the health measure accordingly
  242.     if(pWorld->ppnWorld[pWorld->ulCharacterLocationX+dx][pWorld->ulCharacterLocationY+dy]==1)
  243.     {
  244.         ulFoodEaten++;
  245.         dHealth++;
  246.     }
  247.     if(pWorld->ppnWorld[pWorld->ulCharacterLocationX+dx][pWorld->ulCharacterLocationY+dy]==3)
  248.     {
  249.         ulPoisonEaten++;
  250.         dHealth--;
  251.     }
  252.  
  253.     //If we can actually move forward (i.e. there's not a wall in front of us) do so
  254.     if(pWorld->ppnWorld[pWorld->ulCharacterLocationX+dx][pWorld->ulCharacterLocationY+dy]!=2)
  255.     {
  256.         pWorld->ppnWorld[pWorld->ulCharacterLocationX][pWorld->ulCharacterLocationY]=0;
  257.         pWorld->ulCharacterLocationX+=dx;
  258.         pWorld->ulCharacterLocationY+=dy;
  259.     }
  260.  
  261.     //If we're running in slow motion, draw the agent in its new position 
  262.     if(nSlowMotion)
  263.     {
  264.         pDC->SelectStockObject(BLACK_PEN);
  265.         pDC->SelectStockObject(BLACK_BRUSH);
  266.         pDC->Rectangle(
  267.                                     int(pWorld->ulCharacterLocationX*dScale),
  268.                                     int(pWorld->ulCharacterLocationY*dScale),
  269.                                     int((pWorld->ulCharacterLocationX+1)*dScale+1),
  270.                                     int((pWorld->ulCharacterLocationY+1)*dScale+1));
  271.  
  272.         //And wait a while before continuing
  273.         unsigned long ulDelay=0;
  274.         do
  275.         {
  276.             ulDelay++;
  277.         }
  278.         while(ulDelay<1e+6);
  279.     }
  280.     delete pDC;
  281.  
  282.     //Update a counter of the number of steps used so far to evaluate the fitness of this individual
  283.     ulEvaluationIteration++;
  284. }
  285.  
  286. void Evaluate(void)
  287. {
  288.     unsigned long i;
  289.  
  290.     SYSTEMTIME StartTime,CurrentTime;
  291.     GetSystemTime(&StartTime);
  292.     unsigned long ulCurrentTime;
  293.  
  294.     //Create a drawing surface
  295.     CClientDC *pDC=new CClientDC((CView*)pView);
  296.  
  297.     //If we've finished evaluating the last chromosome and need a new one
  298.     if(ulEvaluationIteration==0)
  299.     {
  300.         unsigned long ulDelay=0;
  301.  
  302.         //This function evaluates the fitness of an individual
  303.         ulFoodEaten=0;
  304.         ulPoisonEaten=0;
  305.  
  306.         pDC->SelectStockObject(WHITE_PEN);
  307.         pDC->Rectangle(500,0,2000,2000);
  308.  
  309.         ulEvaluationIteration=0;
  310.         ulIteration++;
  311.         dHealth=0.0;
  312.  
  313.         //If we're not running in slow motion (i.e. actually watching the agent move around)
  314.         if(!nSlowMotion)
  315.         {
  316.             //Get a new chromosome from the optimisation algorithm so that its performance can be evaluated
  317.             pdChromosome=pOptimiser->pdGetChromosomeForEvaluation();
  318.         }
  319.         else
  320.         {
  321.             //If we are watching teh agent, redraw the world
  322.             pWorld->Draw(pDC);
  323.  
  324.             //And get the best performing chromosome (because we want to see how the best behaves)
  325.             pdChromosome=pOptimiser->pdGetBestChromosome();
  326.         }
  327.     }
  328.     //This section of interprets the chromosome as a series of rules. Each rule consists of eight genes. The first
  329.     //controls whether the rule always fires when its antecedent is true, or only with probability 0.5. The second, third 
  330.     //forth and fifth form the rest of the antecednt and try to match the object that the agent can see to the four types
  331.     //space, food, poison, and wall. The matches and ANDed in the antecedent and hence a rule only fires if they all
  332.     //match. Genes six to eight form the consequent - what the agent does if the rule fires. This can be either turn
  333.     //left, turn right or move forward. Finally, there are three genes that specify a deafult action that is to be taken when
  334.     //none of the rules fire. 
  335.     unsigned long ulBaseGene;
  336.     int nRuleFires=0;
  337.     int nFireDefaultRule=0;
  338.  
  339.     unsigned long ulStartTime=    
  340.                                                     StartTime.wMilliseconds
  341.                                                     +1000*StartTime.wSecond
  342.                                                     +60*StartTime.wMinute
  343.                                                     +3600*StartTime.wHour
  344.                                                     +24*3600*StartTime.wDay;
  345.     do
  346.     {
  347.         nFireDefaultRule=1;
  348.  
  349.         //For each rule
  350.         for(i=0;i<ulNumberOfRules;i++)
  351.         {
  352.             ulBaseGene=i*8;
  353.             nRuleFires=1;
  354.  
  355.             //If gene zero is set, the rule is stochastic and may not fire even its antecedent is true
  356.             if(pdChromosome[ulBaseGene]==1 && rand()%2==0)
  357.             {
  358.                 nRuleFires=0;
  359.             }
  360.  
  361.             //Match what the agent can see to the rule antecedents to see which fire
  362.             if(i/4==0)
  363.             {
  364.                 if(pdChromosome[ulBaseGene+1]!=(nLookForward()==0))
  365.                 {
  366.                     nRuleFires=0;
  367.                 }
  368.                 if(pdChromosome[ulBaseGene+2]!=(nLookForward()==1))
  369.                 {
  370.                     nRuleFires=0;
  371.                 }
  372.                 if(pdChromosome[ulBaseGene+3]!=(nLookForward()==2))
  373.                 {
  374.                     nRuleFires=0;
  375.                 }
  376.                 if(pdChromosome[ulBaseGene+4]!=(nLookForward()==3))
  377.                 {
  378.                     nRuleFires=0;
  379.                 }
  380.             }
  381.             if(i/4==1)
  382.             {
  383.                 if(pdChromosome[ulBaseGene+1]!=(nLookLeft()==0))
  384.                 {
  385.                     nRuleFires=0;
  386.                 }
  387.                 if(pdChromosome[ulBaseGene+2]!=(nLookLeft()==1))
  388.                 {
  389.                     nRuleFires=0;
  390.                 }
  391.                 if(pdChromosome[ulBaseGene+3]!=(nLookLeft()==2))
  392.                 {
  393.                     nRuleFires=0;
  394.                 }
  395.                 if(pdChromosome[ulBaseGene+4]!=(nLookLeft()==3))
  396.                 {
  397.                     nRuleFires=0;
  398.                 }
  399.             }
  400.             if(i/4==2)
  401.             {
  402.                 if(pdChromosome[ulBaseGene+1]!=(nLookRight()==0))
  403.                 {
  404.                     nRuleFires=0;
  405.                 }
  406.                 if(pdChromosome[ulBaseGene+2]!=(nLookRight()==1))
  407.                 {
  408.                     nRuleFires=0;
  409.                 }
  410.                 if(pdChromosome[ulBaseGene+3]!=(nLookRight()==2))
  411.                 {
  412.                     nRuleFires=0;
  413.                 }
  414.                 if(pdChromosome[ulBaseGene+4]!=(nLookRight()==3))
  415.                 {
  416.                     nRuleFires=0;
  417.                 }
  418.             }
  419.  
  420.             //If the rule fires
  421.             if(nRuleFires)
  422.             {
  423.                 //the default won't
  424.                 nFireDefaultRule=0;
  425.  
  426.                 //Decide what action to take based on the genes in the consequent
  427.                 if(pdChromosome[ulBaseGene+5]==1 && pdChromosome[ulBaseGene+6]==0)
  428.                 {
  429.                     TurnLeft();
  430.                 }
  431.                 if(pdChromosome[ulBaseGene+5]==0 && pdChromosome[ulBaseGene+6]==1)
  432.                 {
  433.                     TurnRight();
  434.                 }
  435.                 if(pdChromosome[ulBaseGene+7]==1)
  436.                 {
  437.                     MoveForward();
  438.                 }
  439.             }
  440.         }
  441.  
  442.         ulBaseGene=i*8;
  443.  
  444.         //If no rules have fired,
  445.         if(nFireDefaultRule)
  446.         {
  447.             //Take the default action
  448.             if(pdChromosome[ulBaseGene]==1 && pdChromosome[ulBaseGene+1]==0)
  449.             {
  450.                 TurnLeft();
  451.             }
  452.             if(pdChromosome[ulBaseGene]==0 && pdChromosome[ulBaseGene+1]==1)
  453.             {
  454.                 TurnRight();
  455.             }
  456.             if(pdChromosome[ulBaseGene+2]==1)
  457.             {
  458.                 MoveForward();
  459.             }
  460.         }
  461.  
  462.         //Keep track of how many steps we've taken in evaluating the performance of the current chromosome
  463.         ulEvaluationIteration++;
  464.  
  465.         //Put some info on the display so we can keep track of our progress
  466.         char pString[1000];
  467.         sprintf(pString,"Food eaten:           %u",ulFoodEaten);
  468.         pDC->TextOut(550,20,pString);
  469.         sprintf(pString,"Poison eaten:         %u",ulPoisonEaten);
  470.         pDC->TextOut(550,40,pString);
  471.         sprintf(pString,"Highest fitness:    %+6.2lf",pOptimiser->dGetBestPerformance());
  472.         pDC->TextOut(550,60,pString);
  473.  
  474.         GetSystemTime(&CurrentTime);
  475.         ulCurrentTime=    
  476.                                                     CurrentTime.wMilliseconds
  477.                                                     +1000*CurrentTime.wSecond
  478.                                                     +60*CurrentTime.wMinute
  479.                                                     +3600*CurrentTime.wHour
  480.                                                     +24*3600*CurrentTime.wDay;
  481.     }
  482.     //Keep going until we've evaluated the current chromosome. Evaluating it over too many iterations takes a lot
  483.     //of time. Evaluating over too few makes the fitness estimates too noisy. Also, since the world starts off full
  484.     //of food and poison, evaluating an agent for too short a time is likely to produce one that performs well only when
  485.     //the world is in that state, but poorly later on once most of the food (and poison) have been eaten. This is a 
  486.     //result of evaluating the agent's fitness in an unrepresentative environment (see the article for more details)
  487.     while(ulCurrentTime-ulStartTime<1000 && ulEvaluationIteration<=5000);
  488.  
  489.     //If we have actually finished
  490.     if(ulEvaluationIteration>5000)
  491.     {
  492.  
  493.         //Reset the counter of the number of step made in evaluating the current program
  494.         ulEvaluationIteration=0;
  495.  
  496.         //Reset the world
  497.         pWorld->Initialise();
  498.  
  499.         //If we're not simple watching the best performing agent
  500.         if(!nSlowMotion)
  501.         {
  502.             //Tell the optimisation algorithm how well the chromosome performed
  503.             pOptimiser->SetFitness(dHealth);
  504.             TRACE("Fitness: %+18.3lf\n",dHealth);
  505.             delete []pdChromosome;
  506.             pdChromosome=NULL;
  507.         }
  508.         else
  509.         {
  510.             //Otherwise, redraw the world in its newly reset status
  511.             pWorld->Draw(pDC);
  512.         }
  513.  
  514.         //If we're not simply watching the best performing agent, occasionally save the optimisation algorithm's status
  515.         if(ulIteration%150==0 && !nSlowMotion)
  516.         {
  517.             pOptimiser->Save("GA.txt");
  518. //            pOptimiser->Save("PBIL.txt");
  519. //            pOptimiser->Save("PS.txt");
  520.         }
  521.     }
  522.     //Don't leak DCs!
  523.     delete pDC;
  524. }
  525.  
  526. BOOL CGAPBILExampleDoc::OnNewDocument()
  527. {
  528.     if (!CDocument::OnNewDocument())
  529.         return FALSE;
  530.  
  531.     // TODO: add reinitialization code here
  532.     // (SDI documents will reuse this document)
  533.  
  534.     return TRUE;
  535. }
  536.  
  537. /////////////////////////////////////////////////////////////////////////////
  538. // CGAPBILExampleDoc serialization
  539.  
  540. void CGAPBILExampleDoc::Serialize(CArchive& ar)
  541. {
  542.     if (ar.IsStoring())
  543.     {
  544.         // TODO: add storing code here
  545.     }
  546.     else
  547.     {
  548.         // TODO: add loading code here
  549.     }
  550. }
  551.  
  552. /////////////////////////////////////////////////////////////////////////////
  553. // CGAPBILExampleDoc diagnostics
  554.  
  555. #ifdef _DEBUG
  556. void CGAPBILExampleDoc::AssertValid() const
  557. {
  558.     CDocument::AssertValid();
  559. }
  560.  
  561. void CGAPBILExampleDoc::Dump(CDumpContext& dc) const
  562. {
  563.     CDocument::Dump(dc);
  564. }
  565. #endif //_DEBUG
  566.  
  567. /////////////////////////////////////////////////////////////////////////////
  568. // CGAPBILExampleDoc commands
  569.